/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// singleton
#include "pattern/singleton.h"
// nx::lock_t
#include "thread/lock.h"
// nx::unref
#include "utility/refer.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

/*
    Default locker parameters
*/

#ifndef NX_LOCKER_TYPE
#define NX_LOCKER_TYPE nx::lock_t
#endif

/*
    a locker for automatic lock/unlock
*/

namespace private_
{
    struct LockerBase_
    {
        virtual ~LockerBase_() {}
    };

    template <class Lock_, typename T>
    struct LockerImpl_ : LockerBase_
    {
        Lock_& lc_;
        T      pm_;

        LockerImpl_(Lock_& lc, T pm) : lc_(lc), pm_(pm) { lc_.lock(unref(pm_)); }
        ~LockerImpl_() { lc_.unlock(unref(pm_)); }
    };

    template <class Lock_>
    struct LockerImpl_<Lock_, void> : LockerBase_
    {
        Lock_& lc_;

        explicit LockerImpl_(Lock_& lc) : lc_(lc) { lc_.lock(); }
        ~LockerImpl_() { lc_.unlock(); }
    };
}

template <class Lock_ = NX_LOCKER_TYPE>
class Locker : NonCopyable
{
public:
    typedef Lock_ lock_t;

private:
    private_::LockerBase_* impl_;

public:
    explicit Locker(lock_t& lc)
    {
        impl_ = nx::alloc<private_::LockerImpl_<lock_t, void> >(lc);
    }
    template <typename T>
    Locker(lock_t& lc, T pm)
    {
        impl_ = nx::alloc<private_::LockerImpl_<lock_t, T> >(lc, pm);
    }
    virtual ~Locker()
    {
        nx::free(impl_);
    }
};

/*
    some auxiliary macros
*/

#ifndef NX_SINGLE_THREAD

#define nx_lockex(Lock_, ...) \
    nx::Locker<Lock_> locker(__VA_ARGS__); (void)locker

#define nx_lockex_sole(Lock_) \
    nx_lockex(Lock_, nx::singleton<Lock_>())

#define nx_lockex_type(Lock_, ...) \
    nx_lockex(Lock_, nx::singleton<__VA_ARGS__, Lock_>())

#else

#define nx_lockex(Lock_, ...)
#define nx_lockex_sole(Lock_)
#define nx_lockex_type(Lock_, ...)

#endif

#define nx_lock(...)        nx_lockex(NX_LOCKER_TYPE, __VA_ARGS__)
#define nx_lock_sole()      nx_lockex_sole(NX_LOCKER_TYPE)
#define nx_lock_type(...)   nx_lockex_type(NX_LOCKER_TYPE, __VA_ARGS__)

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
